package com.boat.starter.jetcache;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.alicp.jetcache.CacheBuilder;
import com.alicp.jetcache.anno.CacheConsts;
import com.alicp.jetcache.anno.config.EnableCreateCacheAnnotation;
import com.alicp.jetcache.anno.config.EnableMethodCache;
import com.alicp.jetcache.anno.support.GlobalCacheConfig;
import com.alicp.jetcache.anno.support.SpringConfigProvider;
import com.alicp.jetcache.embedded.EmbeddedCacheBuilder;
import com.alicp.jetcache.embedded.LinkedHashMapCacheBuilder;
import com.alicp.jetcache.redis.RedisCacheBuilder;
import com.alicp.jetcache.support.FastjsonKeyConvertor;
import com.boat.starter.jetcache.constant.CacheConstant;
import com.boat.starter.jetcache.constant.CacheConstant.BoatCacheType;
import com.boat.starter.jetcache.props.CacheProperties;
import com.boat.starter.jetcache.props.JetcacheProperties;
import com.boat.starter.jetcache.service.JetcacheContext;
import com.boat.starter.jetcache.service.RedisLockService;
import com.boat.starter.jetcache.service.SafeJedisService;
import com.boat.starter.jetcache.util.SerializationUtils;

import cn.hutool.core.lang.Assert;
import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.JedisPool;

/**
 * jetcache启动类
 * 
 * @author 周江
 *
 */
@Slf4j
@Configuration
//@EnableMethodCache(basePackages = { "com.boat"})
@EnableCreateCacheAnnotation
@ConditionalOnProperty(prefix = CacheConstant.PREFIX, value = "type", havingValue = BoatCacheType.JETCACHE)
@EnableConfigurationProperties(CacheProperties.class)
public class JetcacheAutoConfiguration implements InitializingBean{

	@Bean
	public SpringConfigProvider springConfigProvider() {
		return new SpringConfigProvider();
	}

	@Bean
	public JedisPool jedisPool(CacheProperties cacheProperties) {
		JetcacheProperties jetcacheProperties = cacheProperties.getJetcache();
		Assert.notEmpty(jetcacheProperties.getRemote().getHost(), "jetcache host未配置或不正确");
		log.info("jetcache初始化开始，host={}, port={}", jetcacheProperties.getRemote().getHost(),
				jetcacheProperties.getRemote().getPort());

		GenericObjectPoolConfig pc = new GenericObjectPoolConfig();
		pc.setMinIdle(jetcacheProperties.getRemote().getPoolMinIdle());
		pc.setMaxIdle(jetcacheProperties.getRemote().getPoolMaxIdle());
		pc.setMaxTotal(jetcacheProperties.getRemote().getPoolMaxTotal());
		return new JedisPool(pc, jetcacheProperties.getRemote().getHost(), jetcacheProperties.getRemote().getPort(),
				jetcacheProperties.getRemote().getTimeout(), jetcacheProperties.getRemote().getPassword(),
				jetcacheProperties.getRemote().getDatabase());
	}

	@Bean
	public GlobalCacheConfig config(JedisPool jedisPool, CacheProperties cacheProperties) {
		JetcacheProperties jetcacheProperties = cacheProperties.getJetcache();
		Map<String, CacheBuilder> localBuilders = new HashMap<>(10);
		EmbeddedCacheBuilder localBuilder = LinkedHashMapCacheBuilder.createLinkedHashMapCacheBuilder()
				.keyConvertor(FastjsonKeyConvertor.INSTANCE)
				.expireAfterWrite(jetcacheProperties.getLocal().getExpireAfterWriteSeconds(), TimeUnit.SECONDS)
				.limit(jetcacheProperties.getLocal().getLimit());
		localBuilders.put(CacheConsts.DEFAULT_AREA, localBuilder);

		Map<String, CacheBuilder> remoteBuilders = new HashMap<>(10);
		RedisCacheBuilder remoteCacheBuilder = RedisCacheBuilder.createRedisCacheBuilder()
				.keyConvertor(SerializationUtils.KEY_SERIALIZER).valueEncoder(SerializationUtils.VALUE_SERIALIZER)
				.valueDecoder(SerializationUtils.VALUE_DESERIALIZER).jedisPool(jedisPool)
				.expireAfterWrite(jetcacheProperties.getRemote().getExpireAfterWriteSeconds(), TimeUnit.SECONDS);
		remoteBuilders.put(CacheConsts.DEFAULT_AREA, remoteCacheBuilder);
		GlobalCacheConfig globalCacheConfig = new GlobalCacheConfig();
		globalCacheConfig.setLocalCacheBuilders(localBuilders);
		globalCacheConfig.setRemoteCacheBuilders(remoteBuilders);
		globalCacheConfig.setStatIntervalMinutes(15);
		// for jetcache <=2.6
		globalCacheConfig.setAreaInCacheName(false); 

		return globalCacheConfig;
	}
	
	@Bean
	public JetcacheContext jetcacheContext() {
		return new JetcacheContext();
	}

	@Bean
	public RedisLockService redisLockService() {
		return new RedisLockService();
	}
	
	@Bean
	public SafeJedisService safeJedisService() {
		return new SafeJedisService();
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		log.info("boat-starter-jetcache加载成功");
	}
}
